package com.lintpanzer.managers
{
	import flash.desktop.NativeApplication;
	import flash.events.IEventDispatcher;
	import flash.events.TimerEvent;
	import flash.text.StyleSheet;
	import flash.utils.Timer;
	
	import mx.controls.TextArea;
	import mx.controls.textClasses.TextRange;
	import mx.core.Application;
	import mx.core.FlexGlobals;
	
	import net.anirudh.as3syntaxhighlight.CodePrettyPrint;
	import net.anirudh.as3syntaxhighlight.PseudoThread;
	
	public class SyntaxHighlighter extends ManagerBase
	{
		private var jsContent:TextArea;
		private var cssString:String =".spl {font-family:sandboxcode;color: #4f94cd;} .str { font-family:sandboxcode; color: #880000; } .kwd { font-family:sandboxcode; color: #000088; } .com { font-family:sandboxcode; color: #008800; } .typ { font-family:sandboxcode; color: #0068CF; } .lit { font-family:sandboxcode; color: #006666; } .pun { font-family:sandboxcode; color: #666600; } .pln { font-family:sandboxcode; color: #222222; } .tag { font-family:sandboxcode; color: #000088; } .atn { font-family:sandboxcode; color: #660066; } .atv { font-family:sandboxcode; color: #880000; } .dec { font-family:sandboxcode; color: #660066; } ";
		private var codeStyle:StyleSheet;
		private var codePrettyPrint:CodePrettyPrint;
		private var codeTimer:Timer;
		private var asyncStop:Boolean;
		private var asyncRunning:Boolean;
		private var codeStylePF:StyleSheet;
		private var srclenPF:int;
		private var arrPF:Array;
		private var lenPF:int;
		private var firstNodePF:Boolean;
		private var firstIndexPF:int;
		private var pfasyncrunning:Boolean;
		private var pfasyncstop:Boolean;
		private var desclenPF:int;
		private var colorThread:PseudoThread;
		[Bindable] private var asyncCodeState:String;
		
		public function SyntaxHighlighter(target:IEventDispatcher=null)
		{
			super(target);
		}
		
		public function highlightSyntax(ta:TextArea):void{
			jsContent = ta;
			codeHighlight();
		}
		
		private function codeHighlight():void
		{
			if ( !codeTimer )
			{
				codeTimer = new Timer(200,1);
				codeTimer.addEventListener(TimerEvent.TIMER, doPrettyPrint);
				
			}	
			
			if ( codeTimer.running )
			{
				codeTimer.stop();
			}
			codeTimer.reset();
			// wait for some time to see if we need to highlight or not
			codeTimer.start();
		}
		
		private function doPrettyPrint(event:TimerEvent=null):void
		{
			if ( !codeStyle )
			{
				codeStyle = new StyleSheet();
				codePrettyPrint = new CodePrettyPrint();
				codeStyle.parseCSS(cssString);
			}
			
			if ( codePrettyPrint.asyncRunning )
			{
				codePrettyPrint.prettyPrintStopAsyc = true;
				FlexGlobals.topLevelApplication.callLater(doPrettyPrint);
				event.updateAfterEvent();
				return;
			}
			
			if ( pfasyncrunning )
			{
				pfasyncstop = true;
				FlexGlobals.topLevelApplication.callLater(doPrettyPrint);
				event.updateAfterEvent();
				return;
			}	
			codeHighlightInPlace();
			
		}
		
		private function pfinit(startIdx:int, endIdx:int):void
		{
			codeStylePF = codeStyle;
			srclenPF = endIdx - startIdx;
			arrPF = codePrettyPrint.mainDecorations;
			lenPF = arrPF.length;
			desclenPF = jsContent.text.length;
			firstNodePF = false;
			firstIndexPF = 0;
			pfasyncrunning = false;
			pfasyncstop = false;	
		}
		
		private function processFormattedCodeAsync(startIdx:int, endIdx:int, completeFn:Function, optIdx:int=0):Boolean
		{			
			if ( pfasyncstop )
			{
				pfasyncrunning = false;
				pfasyncstop = false;
				return false;
			}
			pfasyncrunning = true;
			if ( arrPF == null || srclenPF < 1 ) 
			{
				pfasyncrunning = false;
				return false;
			}
			var tr:TextRange;
			var thecolor:Object;
			var i:int = optIdx;
			if ( i > 0 && i % 5 == 0 )
			{
				asyncCodeState = "Coloring (" + int((i / lenPF) * 100) + "%)...";
			}
			if ( i < lenPF )
			{
				/* find first node */
				if ( arrPF[i] == 0 && firstNodePF == false )
				{        
					firstNodePF = true;					
					return true;
				}
				else if ( arrPF[i] == 0 && firstNodePF == true )
				{
					firstNodePF = false;
					firstIndexPF = i;
					
				} 
				if ( i - 2 > 0 )
				{
					if ( arrPF[i-2]  != arrPF[i] && arrPF[i] < jsContent.text.length )
					{
						tr = new TextRange(jsContent, false, arrPF[i-2] + startIdx, arrPF[i] + startIdx);
						thecolor = codeStylePF.getStyle("." + arrPF[i-1]).color;
						tr.color = thecolor;
					}
					
				}
				return true;
				
				
			}
			if ( i > 0 )
			{
				i -= 2;
				if ( arrPF[i] + startIdx < endIdx )
				{
					tr = new TextRange(jsContent, false, arrPF[i] + startIdx, endIdx);
					thecolor = codeStylePF.getStyle("." + arrPF[i+1]).color;            
					var totlen:int = jsContent.text.length;
					if ( totlen >= endIdx )
						tr.color = thecolor;
					
				}
			}
			if ( completeFn != null )
				completeFn();
			pfasyncrunning = false;
			return false;			
			
		}
		
		private function codePFComplete():void
		{
			asyncCodeState = "";
		}
		
		private function codeInPlaceComplete():void
		{	
			asyncCodeState = "Coloring...";
			if ( pfasyncrunning )
			{
				pfasyncstop = true;
				FlexGlobals.topLevelApplication.callLater(codeInPlaceComplete);
				return;
			}
			asyncRunning = false;
			
			pfinit(0, jsContent.text.length);
			colorThread = new PseudoThread(FlexGlobals.topLevelApplication.systemManager, processFormattedCodeAsync, this, [0, jsContent.text.length, codePFComplete, 0], 3, 2);
		}
		
		private function lexInt(idx:int, total:int):void
		{
			if ( idx > 0 && idx % 5 == 0 )
			{
				asyncCodeState = "Lexing (" + int((idx / total) * 100) + "%)...";
			}
		}
		
		private function codeHighlightInPlace():void
		{
			asyncRunning = true;
			asyncCodeState = "Lexing...";
			codePrettyPrint.prettyPrintAsync(jsContent.text, null, codeInPlaceComplete, lexInt, FlexGlobals.topLevelApplication.systemManager);
			
		}
	}
}